#Api Docs# Api Docs Pre-study and Practice in Golang

#Api Docs# Api Docs Pre-study and Practice in Golang

If you write code, write docs. – Make up by myself


目录 Table of Contents


背景

作为一个轻度强迫症候群者,单纯觉得人工写文档也太让人难受了(还有点累。其次是希望自己可以养成先出文档后写代码的好习惯,所以就迫切需要一个提高生产效率的文档生成工具了。

总的来说,我们希望解决以下问题:

  1. 写文档就像填表格,固定的输入规则对应固定的输出格式

  2. 整体应用文档应有这些内容:

    • 接口目录

    • 变更记录

    • 其它备注

  3. 具体接口文档应有这些内容:

    • 功能描述
    • 请求方法
    • 请求路径
    • 请求参数(规格和示例)
    • 响应状态
    • 响应参数(规格和示例)
  4. 最好具备一定的接口测试功能

技术选型

编码后写

手写 markdown 文档:格式和内容容易出错,写完代码后再写文档积极性也不高,不推荐这样做。

测试时写

postman + docgen:可以实现在线发布接口文档和离线导出接口文档。优点在于无侵入和允许可视化操作,缺点在于请求参数和响应参数无法增加规格描述。

编码前写

swaggo + swagger-markdown:可以实现在线发布接口文档和离线导出接口文档。优点在于同时满足接口文档格式、内容和测试等方面的要求,缺点在于有侵入即需要改造原有项目。

安装工具

安装及使用 swaggo 工具:

1
2
3
4
5
6
7
8
9
10
# 安装
go get github.com/swaggo/swag/cmd/swag
go get -u github.com/swaggo/gin-swagger # 适用于 gin 的工程
go get -u github.com/swaggo/gin-swagger/swaggerFiles # 适用于 gin 的工程

# 使用
swag init # 在工程根目录下执行
# -g ${path} 解析文件路径
# -d ${path} 解析目录路径(默认解析 main.go)
# -o ${path} 输出文档路径

安装及使用 swagger-markdown 工具:

1
2
3
4
5
# 安装
npm install -g swagger-markdown

# 使用
swagger-markdown -i ${input_yaml} -o ${outputMarkdown}

编写文档

整体应用

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"go-swagger-sample/router"
"log"
)

// 注解对于导出 markdown 格式生效
// @title Title For Go-Swagger-Sample Api Docs // 标题
// @description `Markdown` Description For Go-Swagger-Sample Api Docs // 描述(支持 Markdown 格式)
// @version 1.0.0 // 版本
// @host 127.0.0.1:8080 // 测试接口的请求地址
// @BasePath /api/v1 // 测试接口的请求前缀
func main() {
route := router.InitRouter()
if err := route.Run(); err != nil {
log.Fatalf("App crashed, err: %v", err)
}
}

router/router.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package router

import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"go-swagger-sample/controller"
"go-swagger-sample/docs"
)

func InitRouter() *gin.Engine {
router := gin.Default()

// 引用对于访问 html 网页生效
docs.SwaggerInfo.Schemes = []string{"http", "https"} // 测试接口的请求协议
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // 路由托管 swagger 静态资源

userGroup := router.Group("/api/v1/users")
{
userGroup.GET("", controller.ListUsers)
userGroup.POST("", controller.CreateUser)
userGroup.GET("/:id", controller.GetUser)
}

return router
}

具体接口

controller/controller.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package controller

import (
"github.com/gin-gonic/gin"
"go-swagger-sample/models"
"net/http"
)

// User godoc
// @Summary 查询用户列表 // 标题
// @Description 查询用户列表描述 // 描述(支持 Markdown 格式)
// @Tags 用户接口 // 分类
// @Id /users
// @Accept json
// @Produce json
// @Param username query string false "登录名称" // 请求参数规格和示例(query)
// @Success 200 {object} models.Users "success" // 响应参数规格和示例
// @Router /users [get] // 测试接口的请求路径和请求方法
func ListUsers(ctx *gin.Context) {
user := &models.User{}
if err := ctx.ShouldBind(&user); err != nil {
ctx.JSON(http.StatusInternalServerError, err)
}

// TODO: list all users

users := []models.User{*user}
res := &models.Users{
TotalCount: 1,
Items: users,
}
ctx.JSON(http.StatusOK, res)
}

// User godoc
// @Summary 创建用户 // 标题
// @Description 创建用户描述 // 描述(支持 Markdown 格式)
// @Tags 用户接口 // 分类
// @Id /users
// @Accept json
// @Produce json
// @Param createBody body models.User true "创建用户请求主体" // 请求参数规格和示例(body)
// @Success 200 {object} models.User "success" // 响应参数规格和示例
// @Router /users [post] // 测试接口的请求路径和请求方法
func CreateUser(ctx *gin.Context) {
user := &models.User{}
if err := ctx.ShouldBind(&user); err != nil {
ctx.JSON(http.StatusInternalServerError, err)
}

// TODO: create a user

ctx.JSON(http.StatusOK, user)
}

// User godoc
// @Summary 查询用户详情 // 标题
// @Description 查询用户详情描述 // 描述(支持 Markdown 格式)
// @Tags 用户接口 // 分类
// @Id /users/:id
// @Accept json
// @Produce json
// @Param id path string true "用户标识" // 请求参数规格和示例(path)
// @Success 200 {object} models.User "success" // 响应参数规格和示例
// @Router /users/{id} [get] // 测试接口的请求路径和请求方法
func GetUser(ctx *gin.Context) {
user := &models.User{}
if err := ctx.ShouldBindUri(&user); err != nil {
ctx.JSON(http.StatusInternalServerError, err)
}

// TODO: get a user

ctx.JSON(http.StatusOK, user)
}

model/dto.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package models

// 请求参数或响应参数规格和示例
type User struct {
Id string `json:"id" form:"id" uri:"id" example:"1"` // 用户标识
Username string `json:"username" form:"username" example:"admin"` // 登录名称
Password string `json:"password" form:"password" example:"123456"` // 登录密码
}

// 请求参数或响应参数规格和示例
type Users struct {
TotalCount int `json:"totalcount" form:"totalcount" example:"0"` // 共计条数
Items []User `json:"items" form:"items" example:""` // 用户列表
}

接口测试

query

api test - query

body

api test - body

path

api test - path

在线查看

访问 http://${ip}:${port}/swagger/index.html 可阅

离线导出

导出 markdown 格式

使用 swagger-markdown 工具将 swagger.yaml 转换为 docs.md

导出 PDF/Word/HTML 格式

使用 Typora 工具将 docs.md 转换为 docs.pdf/docs.docx/docs.html

附录


Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×